package com.mybatis.jpa.core;

import com.baomidou.mybatisplus.annotation.TableName;
import com.mybatis.jpa.annotation.MapperDefinition;
import com.mybatis.jpa.common.scanner.AnnotationTypeFilterBuilder;
import com.mybatis.jpa.common.scanner.SpringClassScanner;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.annotation.MethodResolver;
import org.apache.ibatis.reflection.ReflectionException;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.type.filter.TypeFilter;

import javax.persistence.Entity;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

/**
 * scanner to MapperDefinition and enhance it</br>
 * execute when an {@code ApplicationContext} gets initialized or refreshed</br>
 * 
 * @author svili
 * @data 2017年5月8日
 *
 */
public class PersistentEnhancerScaner implements InitializingBean,ApplicationContextAware {

	private static final Logger LOGGER = LoggerFactory.getLogger(PersistentEnhancerScaner.class);

	/** 初始化参数:mapper package base place */
	private String mapperPackage;

	/** 初始化参数:entity package base place */
	private String entityPackage;

	private boolean isInit = false;

	private ApplicationContext applicationContext;

	/** 初始化参数:sqlSessionFactory */
	private SqlSessionFactory sqlSessionFactory;

	/** 如果设置此参数那么po是此参数的子类的才处理*/
	private Class<?> superDOClass;

	public void setSuperDOClass(Class<?> clazz)
	{
		superDOClass = clazz;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	public void setMapperPackage(String mapperPackage) {
		this.mapperPackage = mapperPackage;
	}

	public void setEntityPackage(String entityPackage) {
		this.entityPackage = entityPackage;
	}

	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = sqlSessionFactory;
	}

	/**
	 * 为mybatis自动生成sql和 resultMap
	 */
	public void autoSqlForMybatis() {

		if(isInit)
		{
			return;
		}
		isInit = true;
		System.out.println("mybatis jpa init start");

		SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
		this.sqlSessionFactory = sqlSessionFactory;

		// mybatis configuration
		Configuration configuration = this.sqlSessionFactory.getConfiguration();

		/** 扫描DO类 **/
		SpringClassScanner entityScanner = new SpringClassScanner.Builder().
				typeFilter(AnnotationTypeFilterBuilder.build(Entity.class)).build();
		String [] pkgs = entityPackage.split(";");
		for(String pkg : pkgs)
		{
		    entityScanner.getScanPackages().add(pkg);
		}
		Set<Class<?>> entitySet = null;
		Set<Class<?>> mpEntitySet = null;
		try {
			entitySet = entityScanner.scan();
			entityScanner.getTypeFilters().clear();
			entityScanner.getTypeFilters().add(AnnotationTypeFilterBuilder.build(TableName.class));
			mpEntitySet = entityScanner.scan();
			if(mpEntitySet!=null)
			{
				entitySet.addAll(mpEntitySet);
			}
		} catch (ClassNotFoundException | IOException e) {
			// log or throw runTimeExp
			throw new RuntimeException(e);
		}
		if (entitySet != null && !entitySet.isEmpty()) {
			//为所有的DO生成resultMap
			for (Class<?> entity : entitySet) {
				//判断当前类是否为superDOClass的子类
				if(superDOClass!=null && (!superDOClass.isAssignableFrom(entity)))
				{
					continue;
				}
				// resultMap enhance
				PersistentResultMapEnhancer resultMapEnhancer = new PersistentResultMapEnhancer(configuration, entity);
				resultMapEnhancer.enhance();

			}
			// parsePendingMethods(configuration);
		}

		/** 扫描需要的DAO/Mapper **/
		TypeFilter typeFilter = AnnotationTypeFilterBuilder.build(MapperDefinition.class);

		SpringClassScanner mapperScanner = new SpringClassScanner.Builder()
				.typeFilter(typeFilter).build();
		pkgs = mapperPackage.split(";");
		for(String pkg : pkgs)
		{
			mapperScanner.getScanPackages().add(pkg);
		}
		Set<Class<?>> mapperSet = null;
		try {
			mapperSet = mapperScanner.scan();
		} catch (ClassNotFoundException | IOException e) {
			// log or throw runTimeExp
			throw new RuntimeException(e);
		}
		if (mapperSet != null && !mapperSet.isEmpty()) {
			// 自动生成sql
			for (Class<?> mapper : mapperSet) {
				// mapper enhance
				try{
					PersistentMapperEnhancer mapperEnhancer = new PersistentMapperEnhancer(configuration, mapper);
					mapperEnhancer.enhance();
				}catch (ReflectionException e){
					System.out.println(e.getMessage());
				}

			}
			parsePendingMethods(configuration);
		}
		System.out.println("mybatis jpa init complete");
	}

	private void parsePendingMethods(Configuration configuration) {
		Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
		synchronized (incompleteMethods) {
			Iterator<MethodResolver> iter = incompleteMethods.iterator();
			while (iter.hasNext()) {
				try {
					iter.next().resolve();
					iter.remove();
				} catch (IncompleteElementException e) {
					// This method is still missing a resource
				}
			}
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		autoSqlForMybatis();
	}
}
